package drds.plus.datanode.select;

import drds.plus.datanode.configuration.DataNodeConfigurationManager;
import drds.plus.datanode.configuration.DataNodeExtraConfiguration;
import drds.plus.datanode.configuration.DataSourceWrapper;
import drds.plus.datanode.executor.Executor;
import drds.plus.datanode.select.equity_manager_select.EquityManager;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * <pre>
 * 用于运行期间主备切换的场景，DBA通常在做完主备切换后才会去修改每个TAtomDataSource的配置。
 * 假设有d0,d1,d2三个库，完成一次切换时对应每个TAtomDataSource的状态快照如下:
 * =====================
 * d0 d1 d2
 * (1) rw readWeight readWeight
 * (2) na readWeight readWeight
 * (3) na rw readWeight
 * (4) readWeight rw readWeight
 * =====================
 * (1)是切换前正常的状态快照，(4)是切换完成后的状态快照，(2)、(3)是中间过程的状态快照。
 * 如果业务系统在状态(1)、(3)、(4)下要求进行更新操作，则更新操作被允许，因为在这三个状态中都能找到一个含有"writeWeight"的db，
 * 如果业务系统在状态(2)下要求进行更新操作，则更新操作被拒绝，抛出异常，因为在这个状态中找不到含有"writeWeight"的db，
 * </pre>
 *
 * @author yangzhu
 */
public class RuntimeWritableSelector extends AbstractSelector {

    private Map<String, DataSourceWrapper> dataSourceWrapperMap = new HashMap<String, DataSourceWrapper>();
    private EquityManager equityDbManager;

    public RuntimeWritableSelector(Map<String, DataSourceWrapper> dataSourceIdToDataSourceWrapperMap, DataNodeExtraConfiguration dataNodeExtraConfiguration) {
        Map<String, Integer> dataSourceIdToWeightMap = new HashMap<String, Integer>(dataSourceIdToDataSourceWrapperMap.size());
        for (String dataSourceId : dataSourceIdToDataSourceWrapperMap.keySet()) {
            dataSourceIdToWeightMap.put(dataSourceId, 10);
        }
        /**
         * 当成一句相关的数据源来进行处理
         */
        this.equityDbManager = new EquityManager(dataSourceIdToDataSourceWrapperMap, dataSourceIdToWeightMap, dataNodeExtraConfiguration);
        this.readable = false; // 只用于写
        this.dataSourceWrapperMap = dataSourceIdToDataSourceWrapperMap;
    }

    public DataSourceWrapper select() {
        for (Map.Entry<String, DataSourceWrapper> entry : dataSourceWrapperMap.entrySet()) {
            if (DataNodeConfigurationManager.isDataSourceAvailable(entry.getValue(), false)) {
                return entry.getValue();
            }
        }
        return null;
    }

    public Map getDataSourceIdToDataSourceHolderMap() {
        return dataSourceWrapperMap;
    }

    protected <T> T selectDataSourceHolderAndGetConnectionWrapperAndExecuteAndRetryIfFailed(Map<DataSource, SQLException> failedDataSourceToSQLExceptionMap, Executor<T> executor, int times, Object... args) throws SQLException {

        return equityDbManager.selectDataSourceHolderAndGetConnectionWrapperAndExecuteAndRetryIfFailed(failedDataSourceToSQLExceptionMap, executor, times, args);
    }

    public DataSourceWrapper get(String dataSourceId) {
        return dataSourceWrapperMap.get(dataSourceId);
    }

    protected DataSourceHolder findDataSourceHolderByDataSourceIndex(String dataSourceIndex) {
        return equityDbManager.findDataSourceHolderByDataSourceIndex(dataSourceIndex);
    }
}
